package org.stars.daostars;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stars.dao.exception.DaoException;
import org.stars.daostars.annotation.Dao;
import org.stars.daostars.core.DaoMap;
import org.stars.daostars.core.DaoStars;
import org.stars.daostars.core.Helper;
import org.stars.daostars.core.SqlParameter;
import org.stars.daostars.core.SqlQuery;
import org.stars.daostars.core.parser.SqlParser;
import org.stars.daostars.core.runtime.DaoQueryRunner;
import org.stars.daostars.core.runtime.DaoRuntime;
import org.stars.daostars.core.runtime.DaoRuntimeHelper;
import org.stars.daostars.core.runtime.EntityBeanRsHandler;
import org.stars.daostars.core.runtime.SimpleBatchQuery;
import org.stars.daostars.core.runtime.SimpleQuery;
import org.stars.daostars.events.DaoEventHandler;
import org.stars.daostars.pipeline.PipelineRuntime;
import org.stars.daostars.sqlmapper.SqlMapper;
import org.stars.database.ConnectionPoolManager;
import org.stars.security.UserIdentity;
import org.stars.util.StringUtility;
import org.stars.util.diagnostic.StackTraceDiagnosticHandler;
import org.stars.util.diagnostic.StackTraceDiagnosticHelper;
import org.stars.util.reflect.ReflectUtil;

/**
 * <p>
 * E' la classe base da cui si derivano tutte le classi <b>DAO</b> (Data Access
 * Object), ovvero tutte le classi che si preoccupano della persistenza degli
 * EntityBean. Implementa i metodi dell'interfaccia <i>Dao</i> ed implementa
 * tutti i metodi dell'interfaccia che non hanno come parametro l'entityBean.
 * <p>
 * Esistono tre modalità nel qual un oggetto DAO opera:
 * <ul>
 * <li><b>Modalità standard</b>: la connessione viene recuperata mediante il
 * metodo <i>getConnection</i> e una volta effettuate tutte le operazioni sul db
 * viene rilasciata.</li>
 * <li><b>Modalità test</b>: è analoga alla modalità standard, le operazioni sul
 * database non vengono fatte seguire da un commit, ma sempre da un rollback,
 * quindi in altre parole: <b>le operazioni non vengono mai resi
 * persistenti</b>. Questa modalità nasce principalmente per permettere il debug
 * dell'applicazione. E' possibile impostare questa modalità mediante il metodo
 * <i>setTestMode(true)</i>. Da notare che finchè non viene richiamato il metodo
 * <i>setTestMode(false)</i> il DAO rimarrà in tale modalità. Per verificare se
 * il DAO è in questo stato, basta invocare il metodo <i>isTestMode</i>; se
 * <b>true</b> indica che siamo in modalità di test.</li>
 * <li><b>Modalità Transazionale</b>: un DAO si trova in questa modalità quando
 * le operazioni che deve compiere si ritrovano all'interno di una transazione
 * più grande rispetto all'esecuzione del comando sql che il dao va ad eseguire.
 * Le transazioni vengono gestite mediante gli oggetti di tipo
 * {@link DaoSession}, quindi si rimanda alla relativa documentazione per
 * ulteriori approndimenti riguardo alle transazioni.</li>
 * </ul>
 * <h2>Operazioni da fare sulle classi derivate da DaoBase</h2>
 * Ogni classe che deriva da DaoBase, deve:
 * <ul>
 * <li><b>Ridefinire le query di SELECT, INSERT, UPDATE, DELETE (SQL_SELECT,
 * SQL_INSERT, SQL_UPDATE, SQL_DELETE)</b>: nella parte statica della classe
 * bisogna inserire la ridefinizione delle 4 query. <b><u>La tabella oggetto del
 * DAO deve avere come alias la lettera 'a' nella query di select</u></b>.Un
 * esempio:
 * <code>public class DaoSesso extends DaoBase {<br>...<br>{<br>queriesSQL.put(
 * <br>SQL_SELECT,<br>"select a.id as id, a.descrizione as descrizione "<br>+"from m_codifica a "<br>+"where codice like 'SESSO%' "<br>
 * ); queriesSQL.put(<br>SQL_INSERT,<br>"insert into m_codifica set id=m_codifica_seq.nextval," +<br>" descrizione=? "+<br>" codice='SESSO' "<br>
 * ); ...<br>}<br>...<br></code></li>
 * <li><b>Implementare i metodi crea(EntityBean), recupera(EntityBean),
 * aggiorna(EntityBean)</b>: in base alle specifiche fornite per il Dao. Un
 * esempio di implementazione di:<br>
 * 
 * <pre>
 * &lt;code&gt;
 *                          ...
 *                          public int recupera(EntityBean entity) throws Exception {
 * &lt;br&gt;
 * EbPersona persona = (EbPersona) entity;
 * &lt;br&gt;
 *                         Object[] params = {StringUtility.nvl(persona.getCognome()),
 * &lt;br&gt;
 *                         StringUtility.nvl(persona.getNome()),
 * &lt;br&gt;
 * StringUtility.nvl(persona.getCodiceFiscale())
 * &lt;br&gt;
 *                         };
 * &lt;br&gt;
 *                         String sWhere=&quot; UPPER(s.COGNOME) like UPPER(? || '%') AND &quot; +
 * &lt;br&gt;
 *                         &quot; UPPER(s.NOME) like UPPER(? || '%') AND &quot; +
 * &lt;br&gt;
 *                         &quot; UPPER(c.CODICE_FISCALE) like UPPER(? || '%') &quot; ;
 * &lt;br&gt;
 * return recuperaSQL(getQuerySQL(SQL_SELECT), sWhere, params);
 * &lt;br&gt;
 *                         }
 * &lt;br&gt;
 *                         ...
 * &lt;br&gt;&lt;/code&gt;
 * </pre>
 * 
 * <br>
 * In questo caso l'implementazione del metodo è semplice in quanto esiste solo
 * una possibilit� di ricercare un entity EbSesso (mediante l'id). Nel caso in
 * cui ci fossero più modalità, è necessario <i>costruire</i> le clausole di
 * where in maniera appropriata. Nel caso di clausole di <b>LIKE</b> i '%'
 * devono essere definiti all'interno del metodo recupera.</li>
 * <br>
 * <li><b>Controllare che TUTTI I METODI DI CREA, RECUPERA, AGGIORNA, ELIMINA
 * funzionino!!!</b></li>
 * </ul>
 * 
 * @author Alex Svetina (908303)
 * @author Francesco Benincasa (908099)
 * 
 * 
 * @version 1.1.0, 13/07/06
 * @param <E>
 * 
 * @see Dao
 * @see DaoSession
 * 
 */
public class DaoBase<E> implements Serializable, DaoSessionWorker {
		
	/**
	 * Getter della definizione del dao.
	 * 
	 * @return definizione del dao.
	 */
	public SqlMapper getDefinition() {
		return sqlMapper;
	}

	/**
	 * se true indica che è abilitato il limite sulle righe in test mode
	 */
	protected boolean enabledRowLimit;

	/**
	 * Rappresenta il formato di default dei campi data.
	 */
	public static final String FORMAT_DATE = "dd/MM/yyyy";

	protected static final int NON_OCI_BLOB_MAX_SIZE = 2000;

	/**
	 * Il query runner della libreria <i>common-dbutil</i> della apache
	 * foundation che viene impiegato normalmente per eseguire le query.
	 */
	public static final DaoQueryRunner QueryRunner = new DaoQueryRunner();

	/**
	 * codice d'errore
	 */
	public static final int RETURN_ERROR = -1;

	/**
	 * serial id
	 */
	protected static final long serialVersionUID = -8609254196047870745L;

	public static final String SQL_SELECT_BLOB = "SQL_SELECT_BLOB";

	public static final String SQL_UPDATE_BLOB = "SQL_UPDATE_BLOB";

	/**
	 * Abilita il test mode per le transazioni
	 */
	protected static boolean testMode;

	/**
	 * estrae la connessione dal connection pool
	 * 
	 * @param pool
	 *            name of connection pool
	 * @return Connection
	 */
	protected static Connection getConnection(String pool) {
		Connection conn;
		try {
			conn = ConnectionPoolManager.getInstance().getConnection(pool);
		} catch (SQLException ex) {
			conn = null;
		} catch (Exception ex) {
			conn = null;
		}
		return conn;
	}

	/**
	 * Restituisce un'istanza di entity bean che specializza il dao. Il nome
	 * della classe che specializza il dao e' contenuto nella proprieta'
	 * <code>entityBeanClass</code>.
	 * <p>
	 * Nel caso di errore viene sollevata un'eccezione.
	 * 
	 * @return istanza dell'entity bean
	 */
	@SuppressWarnings("unchecked")
	protected E newBeanInstance() {
		E ret = null;
		try {
			// possiamo fare così perchè nel costruttore abbiamo definito il
			// nome della classe
			ret = (E) Class.forName(entityBeanClassName).newInstance();
		} catch (Exception e) {
			Log log = getLog();
			log.fatal("The creation of the bean " + entityBeanClassName + " is impossible. Reason: " + e.toString());
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * Indica se e' stato definita la modalità global print mode
	 * 
	 * @return <code>true</code> se è abilitato il global print mode
	 */
	public static boolean isGlobalPrintMode() {
		return globalPrintMode;
	}

	/**
	 * <p>
	 * Restituisce <code>true</code> nel caso l'oggetto si trova in modalit�
	 * <i>test</i>, <code>false</code> se sta operando in modalit� normale.
	 * 
	 * @return Returns the testMode.
	 * 
	 */
	public static boolean isTestMode() {
		return testMode;
	}

	/**
	 * setter del global print mode
	 * 
	 * @param value
	 *            nuovo valore
	 */
	public static void setGlobalPrintMode(boolean value) {
		globalPrintMode = value;
	}

	/**
	 * Imposta la modalita'a' test. Se impostata a <code>true</code>, tutte le
	 * operazioni di <b>insert, update e delete</b> non verranno rese
	 * persistenti su db. Per tornare alla modalita'� normale e' sufficiente
	 * impostare come parametro di questo metodo <code>false</code>.
	 * 
	 * @param value
	 *            modalita'� da impostare
	 */
	public static void setTestMode(boolean value) {
		Log log = LogFactory.getLog(DaoBase.class);
		log.info("Test mode is " + value);
		testMode = value;
	}

	/**
	 * colonne restituite dal result set.
	 */
	private String[] columns;

	/**
	 * nome della connessione usata in modalita'� non transazionale.
	 */
	protected String connectionName;

	/**
	 * Nome della classe Dao.
	 */
	protected String daoClassName;

	/**
	 * Definzione del dao, inteso come sql, parametri, mappe e sql condizionale.
	 */
	protected SqlMapper sqlMapper;

	/**
	 * eventuale sessione di lavoro. Viene impostato dalla stessa sessione per
	 * fare in modo che ogni dao in questione abbia un riferimento alla sessione
	 * che lo sta utilizzando
	 */
	protected DaoSession daoSession;

	/**
	 * Elenco. E' l'elenco dei bean ottenuti mediante interrogazione su db.
	 */
	protected List<E> elencoBean;

	/**
	 * Iterator sull'elenco
	 */
	protected Iterator<E> iteratorElencoBean;

	/**
	 * Getter dell'iterator sull'elenco
	 * 
	 * @return iterator
	 */
	public final Iterator<E> getIteratorElencoBean() {
		return iteratorElencoBean;
	}

	/**
	 * Resetta iterator
	 */
	public final void resetIteratorElencoBean() {
		iteratorElencoBean = elencoBean.iterator();
	}

	/**
	 * tipo dell'entity gestito dal dao.
	 */
	protected Class<E> entityBeanClass;

	/**
	 * Nome della classe dell'entity bean gestito dal Dao
	 */
	protected String entityBeanClassName;

	private String language;

	protected final Pattern LANGUAGE_TOKEN = Pattern.compile("\\[%LANG%\\]");

	/**
	 * gestore degli eventi dao
	 */
	protected DaoEventHandler eventHandler;

	protected String orderBy;

	protected String orderByName;

	/**
	 * Ultimo elemento recuperato da un'operazione di SELECT
	 */
	protected E dettaglioBean;

	/**
	 * traduttore di eccezione
	 */
	protected SQLExceptionTranslator exceptionTranslator;

	/**
	 * 
	 */
	protected ParameterConverter parameterConverter;

	/**
	 * Abilita il display delle quesry eseguite
	 */
	protected boolean printMode;

	/**
	 * gestore della visualizzazione dello stack trace.
	 */
	protected StackTraceDiagnosticHandler stackTraceDiagnosticHandler;

	/**
	 * Connessione utilizzata in caso di transazione.
	 */
	protected transient Connection transaction;

	/**
	 * L'utente che sta utilizzando tale dao. Quando viene creato il dao,
	 * l'utente viene impostato a <code>null</code>.
	 */
	protected UserIdentity user;

	protected Map<String, String> variables;

	/**
	 * Costruttore di base. Definito solo per essere compatibile con le
	 * specifiche JavaBean
	 */
	public DaoBase() {

	}

	/**
	 * @param definition
	 * @param eType
	 */
	public DaoBase(String definition, Class<E> eType) {
		setUp(definition, eType);
		connectionName = sqlMapper.getDefaultConnectionName();
	}

	/**
	 * @param definition
	 * @param eType
	 * @param locale
	 */
	public DaoBase(String definition, Class<E> eType, Locale locale) {
		setUp(definition, eType);
		setLanguage(locale);
	}

	/**
	 * @param definition
	 * @param eType
	 * @param locale
	 * @param connectionName
	 */
	public DaoBase(String definition, Class<E> eType, Locale locale, String connectionName) {
		setUp(definition, eType);
		setLanguage(locale);
		this.connectionName = connectionName;
	}

	/**
	 * Costruisce il dao e utilizza la connessione di default della definizione
	 * del dao.
	 * 
	 * @param eType
	 */
	public DaoBase(Class<E> eType) {
		setUp(eType);
		connectionName = sqlMapper.getDefaultConnectionName();
	}

	/**
	 * @param eType
	 * @param locale
	 */
	public DaoBase(Class<E> eType, Locale locale) {
		setUp(eType);
		setLanguage(locale);
	}

	/**
	 * @param eType
	 * @param locale
	 * @param connectionName
	 */
	public DaoBase(Class<E> eType, Locale locale, String connectionName) {
		setUp(eType);
		setLanguage(locale);
		this.connectionName = connectionName;
	}

	/**
	 * Costruisce il dao e definisce la connessione sulla quale si basa.
	 * 
	 * @param eType
	 * @param connectionName
	 */
	public DaoBase(Class<E> eType, String connectionName) {
		setUp(eType);
		this.connectionName = connectionName;
	}

	/**
	 * Crea un nuovo entity bean nel dettaglio in base al nome della classe
	 * definita in <code>entityBeanClassName</code>
	 */
	protected void createResultBean() {
		dettaglioBean = newBeanInstance();
	}

	/**
	 * @param queryName
	 * @param dto
	 * @return
	 * @throws Exception
	 */
	public final int execute(String queryName, Object dto) throws Exception {
		return execute(queryName, null, dto, null);
	}

	/**
	 * @param queryName
	 * @param dto
	 * @param indexedParameters
	 * @return
	 * @throws Exception
	 */
	public final int execute(String queryName, EntityBean dto, Object[] indexedParameters) throws Exception {
		return execute(queryName, null, dto, indexedParameters);
	}

	/**
	 * 
	 * @param queryName
	 * @param moreSql
	 * @param eb
	 * @param indexedParameters
	 * @return numero di elementi aggiornati
	 * @throws Exception
	 */
	public int execute(String queryName, String moreSql, Object dto, Object[] indexedParameters) throws Exception {
		Log log = getLog();
		SimpleQuery ret = null;

		if (dto == null) {
			dto = newBeanInstance(); // se non esiste lo creiamo del tipo che
			// specializza il dao
		}

		if (indexedParameters == null)
			indexedParameters = new Object[0];

		// prendiamo da eb i nomi degli attributi in esso definiti
		if (sqlMapper == null) {
			DaoException ex = new DaoException("Impossibile trovare la definizione del dao " + daoClassName);
			log.error(ex);
			throw (ex);
		}

		SqlQuery query = sqlMapper.getQuery(queryName);
		if (query == null) {
			DaoException ex = new DaoException("Impossibile trovare nel dao " + daoClassName + " la definizione della query " + queryName);
			log.error(ex);
			throw (ex);
		}

		SqlQuery moreSqlQuery = null;

		moreSql = StringUtility.nvl(moreSql);
		if (moreSql.length() > 0) {
			moreSqlQuery = SqlParser.parseQuery(moreSql);
		}

		if (!query.isPreconditionRespected(user, dto, indexedParameters, variables)) {
			DaoException ex = new DaoException("In the dao definition " + daoClassName + " the precondition " + query.getPrecondition() + " for the query " + query.getName()
					+ " is not satisfeid!");
			log.error(ex);
			throw (ex);
		}

		ret = DaoRuntime.getSimpleQuery(parameterConverter, user, dto, indexedParameters, variables, query, moreSqlQuery, null);

		return executeSQL(queryName, ret.preparedSql, ret.preparedParameters, ret.preparedParameterNames);
	}

	/**
	 * Esegue delle query in modalità batch. Normalmente questo metodo
	 * restituisce il numero di elementi inseriti. Per modalità batch si intende
	 * eseguire la stessa query ma con parametri differenti.
	 * <p>
	 * Con i driver oracle questo metodo restituisce -2 per indicare che il
	 * numero di elementi aggiornato non è definibile
	 * 
	 * @param queryName
	 *            nome della query da eseguire
	 * @param eb
	 *            array di entity bean che contengono i parametri delle query
	 * @return numero di elementi aggiornati o inseriti
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int executeBatch(String queryName, Object[] eb) throws Exception {
		return executeBatch(queryName, null, eb, null, false);
	}

	/**
	 * Esegue delle query in modalità batch. Normalmente questo metodo
	 * restituisce il numero di elementi inseriti. Per modalità batch si intende
	 * eseguire la stessa query ma con parametri differenti.
	 * <p>
	 * Con i driver oracle questo metodo restituisce -2 per indicare che il
	 * numero di elementi aggiornato non è definibile
	 * 
	 * @param queryName
	 *            nome della query da eseguire
	 * @param moreSql
	 *            codice sql da aggiungere alla query
	 * @param eb
	 *            array di entity bean che contengono i parametri delle query
	 * @param indexedParameters
	 *            array dei parametri indicizzati
	 * @return numero di elementi aggiornati o inseriti
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int executeBatch(String queryName, String moreSql, Object[] eb, Object[][] indexedParameters) throws Exception {
		return executeBatch(queryName, moreSql, eb, indexedParameters, false);
	}

	/**
	 * @param queryName
	 * @param moreSql
	 * @param listEb
	 * @param indexedParameters
	 * @param bTransactionMode
	 * @return numero di elementi aggiornati o inseriti
	 * 
	 * @throws Exception
	 */
	public int executeBatch(String queryName, String moreSql, Object[] listEb, Object[][] indexedParameters, boolean bTransactionMode) throws Exception {

		// Object[] indexedParameters

		Log log = getLog();
		// parametri delle query batch
		Object[][] batchValues = null;

		// array dei parametri indicizzati relativi al parametro 0
		Object[] indexedParametersIndex0 = null;

		if (listEb == null) {
			listEb = new EntityBean[1];
			listEb[0] = newBeanInstance();
		}
		// ASSERT: ora eb ha almeno un elemento

		if (indexedParameters == null || indexedParameters.length == 0) {
			// ASSERT: eb con almeno un elemento ed eb senza parametri
		} else {
			// Dobbiamo verificare che le dimensioni dei due array
			// siano uguali altrimenti lanciamo eccezione
			if (listEb.length != indexedParameters.length) {
				String msg = "Query parameters  " + queryName + " in the dao " + this.getDaoClassName() + " are incosistent. Check the lenght of the parameters array.";
				log.error(msg);
				throw (new DaoException(msg));
			}

			indexedParametersIndex0 = indexedParameters[0];
		}

		// ASSERT: siamo in una delle due situazioni
		// 1 - Eb definito e array indexParameter null
		// 2 - Eb definito e array indexParameter con la stessa lunghezza

		// prendiamo da eb i nomi degli attributi in esso definiti
		if (sqlMapper == null) {
			String msg = "It is impossible to find the dao definition " + daoClassName;
			throw (new DaoException(msg));
		}
		SqlQuery query = sqlMapper.getQuery(queryName);
		if (query == null) {
			String msg = "For the dao " + daoClassName + " does not exist the query " + queryName;
			log.error(msg);
			throw (new DaoException(msg));
		}

		SqlQuery moreSqlQuery = null;

		// prendiamo il codice sql
		String sql = query.getSql(user, listEb[0], indexedParametersIndex0, variables);
		List<SqlParameter> sqlParameters = query.getParameters(user, listEb[0], indexedParametersIndex0, variables);

		moreSql = StringUtility.nvl(moreSql);
		SimpleBatchQuery batchRet;
		if (moreSql.length() > 0) {
			StringBuffer sb = new StringBuffer();
			sb.append(sql);

			moreSqlQuery = SqlParser.parseQuery(moreSql);

			sb.append(moreSqlQuery.getSql(user, listEb[0], indexedParametersIndex0, variables));
			List<SqlParameter> tempParams;

			tempParams = moreSqlQuery.getParameters(user, listEb[0], indexedParametersIndex0, variables);
			if (tempParams != null)
				sqlParameters.addAll(tempParams);

			// ridefiniamo codice sql
			sql = sb.toString();
		}

		if (!query.isPreconditionRespected(user, listEb[0], indexedParametersIndex0, variables)) {
			String msg = "In the dao definition " + daoClassName + " the precondition " + query.getPrecondition() + " for the query " + query.getName() + " is not satisfeid!";
			log.error(msg);
			DaoException ex = new DaoException(msg);
			throw (ex);
		}

		batchRet = DaoRuntime.getSimpleBatchQuery(queryName, parameterConverter, user, listEb, indexedParameters, variables, query, moreSqlQuery);

		int ret = 0;
		if (bTransactionMode) {
			// impostiamo la connessione
			DaoSession session = new DaoSession(this.connectionName);

			try {
				session.beginTransation();
				session.addDao(this);
				ret = executeBatchSQL(sql, batchValues);
				session.commitTransaction();
			} catch (Exception e) {
				session.rollbackTransaction();
				throw (e);
			}

		} else {
			ret = executeBatchSQL(batchRet.preparedSql, batchRet.preparedParameters);
		}

		return ret;
	}

	/**
	 * Esegue un codice sql
	 * 
	 * @param sql
	 *            codice sql
	 * @param parms
	 *            array di array di parametri
	 * @return numero di elementi aggiornati. Da specifiche jdbc nel caso in cui
	 *         non si sappia quanti elementi sono stati modificati, viene
	 *         restituito -2.
	 * @throws Exception
	 */
	protected final int executeBatchSQL(String sql, Object[][] parms) throws Exception {
		Log log = getLog();
		int rc = 0;
		int[] nRigheAggiornate;
		Connection cn = null;

		// facciamo un controllo, se sql non contiene un codice valido, allora
		// usciamo senza far nulla
		sql = StringUtility.nvl(sql).trim();
		if (sql.length() == 0) {
			return 0;
		}

		try {
			cn = (this.transaction == null ? getConnection() : this.transaction);

			if (isTestMode() && this.transaction == null) { 
				// ASSERT: siamo in testMode e non siamo in una transazione complessa
				cn.setAutoCommit(false);
			}

			// processiamo la lingua
			sql = processLanguage(sql); 

			printDebugStack();
			printConnectionInfo(cn, transaction!=null);
			if (parms != null) {
				for (int i = 0; i < parms.length; i++) {
					printDebugSQL("Execute batch SQL: " + sql, parms[i], null);
				}
			}

			nRigheAggiornate = QueryRunner.batch(cn, sql, parms);

			for (int j = 0; j < nRigheAggiornate.length; j++) {
				printDebugSQL("# of rows updated: " + nRigheAggiornate[j]);

				if (nRigheAggiornate[j] == -2 || rc == -2) {
					rc = -2; // come specifiche jdbc, che è andato tutto bene
					// ma non sappiamo quanti elementi sono stati
					// modificati.
				} else {
					rc += nRigheAggiornate[j];
				}
			}

		} catch (SQLException e) {
			log.fatal(e.getMessage());
			throw (new SQLException(e.getMessage()));
		} catch (Exception e) {
			log.fatal(e.getMessage());
			throw (new Exception(e.getMessage()));
		} finally {
			if (isTestMode() && this.transaction == null) { // ASSERT: siamo in
				// testMode e non
				// siamo in una
				// transazione
				// complessa,
				// se siamo in testmode dobbiamo fare il rollback
				DbUtils.rollback(cn);
				cn.setAutoCommit(true);
				DbUtils.closeQuietly(cn);
			} else if (this.transaction == null) {
				DbUtils.closeQuietly(cn);
			}
		}
		return rc;
	}

	protected final int executeSQL(String sql, Object[] parms) throws Exception {
		return executeSQL(null, sql, parms, null);
	}

	/**
	 * Esegue un INSERT, UPDATE o DELETE
	 * 
	 * @param sqlName
	 *            indica il nome della query che si sta eseguendo. Serve per i
	 *            mesasggi di diagnostica. Può essere anche messo a
	 *            <code>null</code>.
	 * @param sql
	 * @param parms
	 * @return numero di elementi aggiornati, modificati o inseriti.
	 * @throws Exception
	 */
	protected final int executeSQL(String sqlName, String sql, Object[] parms, List<SqlParameter> paramNames) throws Exception {
		Log log = getLog();
		int rc = 0;
		int nRigheAggiornate;
		Connection cn = null;

		sqlName = StringUtility.nvl(sqlName);
		// facciamo un controllo, se sql non contiene un codice valido, allora
		// usciamo senza far nulla
		sql = StringUtility.nvl(sql).trim();
		if (sql.length() == 0) {
			String sMsg = "WARNING Dao: " + daoClassName + " Query: " + StringUtility.nvl(sqlName, "<Sconosciuta>")
					+ " The sql string is empty. Perhaps the name of the query is wrong?";
			printDebugSQL(sMsg);
			return 0;
		}

		try {
			cn = (this.transaction == null ? getConnection() : this.transaction);

			if (isTestMode() && this.transaction == null) { // ASSERT: siamo in
				// testMode e non
				// siamo in una
				// transazione
				// complessa
				cn.setAutoCommit(false);
			}

			sql = processLanguage(sql); // processiamo la lingua

			printDebugStack();
			printConnectionInfo(cn, transaction!=null);
			printDebugSQL("Dao: " + daoClassName + " Query: " + StringUtility.nvl(sqlName, "<Unkown>") + " " + " execute SQL: " + sql, parms, paramNames);

			nRigheAggiornate = QueryRunner.update(cn, sql, parms);
			rc = nRigheAggiornate;

			printDebugSQL("# of rows updated: " + nRigheAggiornate);

		} catch (SQLException e) {
			log.fatal(e.getMessage());
			throw (exceptionTranslator.translate(sql, e, DaoRuntimeHelper.getDatabaseInformation(cn)));
		} catch (Exception e) {
			log.fatal(e.getMessage());
			throw (new Exception(e.getMessage()));
		} finally {
			if (isTestMode() && this.transaction == null) {
				// ASSERT: siamo in testMode e non siamo in una
				// transazione complessa, se siamo in testmode
				// dobbiamo fare il rollback
				DbUtils.rollback(cn);
				cn.setAutoCommit(true);
				DbUtils.closeQuietly(cn);
			} else if (this.transaction == null) {
				DbUtils.closeQuietly(cn);
			}
		}
		return rc;
	}

	/**
	 * Recupera l'elenco delle colonne recuperate dall'ultimo result set.
	 * 
	 * @return array di stringhe delle colonne recuperate
	 */
	public String[] getColumns() {
		return columns;
	}

	/**
	 * Restituisce una connessione.
	 * 
	 * @return Connection
	 */
	protected Connection getConnection() {
		return getConnection(connectionName);
	}

	/**
	 * Getter dell'attributo daoClassName.
	 * 
	 * @return the daoClassName
	 */
	public String getDaoClassName() {
		return daoClassName;
	}

	/**
	 * getter del dao session
	 * 
	 * @return the daoSession
	 */
	@Override
	public DaoSession getDaoSession() {
		return daoSession;
	}

	/**
	 * restituisce il dettaglioBean, ovvero il bean recuperato dall'ultima
	 * operazione di lettura.
	 * 
	 * @return EntityBeanBase
	 */
	public E getDettaglioBean() {
		E app = null;
		try {
			// se con la query precedente abbiamo trovato qualcosa
			// prendiamo il dettaglioBean, altrimenti ne creiamo
			// uno.
			if (elencoBean.size() > 0) {
				app = (E) dettaglioBean;
			} else {
				Log log = getLog();
				log.warn("WARNING Dao: The last query called in dao " + daoClassName + " has no result. The bean returned was an empty bean!");
				app = newBeanInstance();
			}
		} catch (Exception e) {
			Log log = getLog();
			log.error("Can not convert bean from " + dettaglioBean.getClass() + " to " + this.entityBeanClassName + ". The method will return a new object.");
			app = newBeanInstance();
		}

		return app;
	}

	/**
	 * Restituisce il dettaglioBean sottoforma di istanza di classe specificata
	 * mediante parametro
	 * 
	 * @param <T>
	 * 
	 * @return List
	 * @param eType
	 * @throws Exception
	 *             in caso di errore
	 */
	@SuppressWarnings("unchecked")
	public <T> T getDettaglioBean(Class<T> eType) throws Exception {
		T app = null;
		try {
			// se con la query precedente abbiamo trovato qualcosa
			// prendiamo il dettaglioBean, altrimenti ne creiamo
			// uno.
			if (elencoBean.size() > 0) {
				app = (T) dettaglioBean;
			} else {
				Log log = getLog();
				log.warn("WARNING Dao: The last query called in dao " + daoClassName + " has no result. The bean returned was an empty bean!");
				app = (T) eType.newInstance();
			}
		} catch (Exception e) {
			Log log = getLog();
			log.error("Can not convert bean from " + dettaglioBean.getClass() + " to " + eType + ". The method will return a new object.");
			app = (T) eType.newInstance();
		}

		return app;

	}

	/**
	 * Restituisce l'elenco dei bean.
	 * 
	 * @return List
	 */
	public List<E> getElencoBean() {
		return elencoBean;
	}

	/**
	 * Restituisce l'elenco dei bean, considerando il fatto che la lista è un
	 * elenco di bean il cui tipo viene passato come argomento al metodo.
	 * 
	 * @param <T>
	 * 
	 * @return List
	 * @param eType
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> getElencoBean(Class<T> eType) {
		return (List<T>) (elencoBean);
	}

	public Class<E> getEntityBeanClass() {
		return entityBeanClass;
	}

	/**
	 * Getter della lingua
	 * 
	 * @return stringa che rappresenta il codice della lingua attualmente
	 *         utilizzata
	 */
	public String getLanguage() {
		return language;
	}

	/**
	 * logger del dao.
	 * 
	 * @return logger
	 */
	public Log getLog() {
		return LogFactory.getLog(DaoBase.class);
	}

	/**
	 * Getter dell'attributo onGetEntityEventHandler.
	 * 
	 * @return the onGetEntityEventHandler
	 */
	public DaoEventHandler getEventHandler() {
		return eventHandler;
	}

	/**
	 * @return the orderBy
	 */
	public String getOrderBy() {
		return orderBy;
	}

	/**
	 * @return the orderByName
	 */
	public String getOrderByName() {
		return orderByName;
	}

	/**
	 * @return the parameterConverter
	 */
	public ParameterConverter getParameterConverter() {
		return parameterConverter;
	}

	/**
	 * recupera i parametri di una query dato il nome di una query.
	 * 
	 * @param key
	 *            query name
	 * 
	 * @return elenco dei parametri.
	 */
	protected List<SqlParameter> getQueryParameters(String key) {
		return sqlMapper.getQueryParameters(key);
	}

	/**
	 * dato il nome di una query, recupera, se esiste, il relativo codice SQL.
	 * Se non lo trova, restituisce una stringa vuota.
	 * 
	 * @param key
	 *            query name
	 * 
	 * @return String di query. Nel caso in cui l'sql non è definito, viene
	 *         restituita una stringa vuota.
	 */
	public String getQuerySQL(String key) {
		return sqlMapper.getQuerySql(key);
	}

	/**
	 * @return the stackTraceDiagnosticHandler
	 */
	public StackTraceDiagnosticHandler getStackTraceDiagnosticHandler() {
		return stackTraceDiagnosticHandler;
	}

	/**
	 * Restituisce la connessione in modalità transazionale. Questa connessione
	 * viene impostata ad un valore diverso da <code>null</code> solo nel caso
	 * ci troviamo all'interno di una connessione.
	 * <p>
	 * 
	 * @return connessione transazionale
	 */
	protected Connection getTransaction() {
		return transaction;
	}

	/**
	 * Getter dell'attributo user
	 * 
	 * @return the user
	 */
	public UserIdentity getUser() {
		return user;
	}

	/**
	 * Recuper il valore di una varibile. Il nome è case insensitive. Nel caso
	 * in cui tale valore non esiste, esso viene restituito null.
	 * 
	 * @param name
	 *            nome della variabile da recuperare
	 * @return valore della variabile.
	 */
	public String getVariable(String name) {
		name = StringUtility.nvl(name).toUpperCase();

		return variables.get(name);
	}

	/**
	 * Se <code>true</code> indica che il dao è utilizzato in una connessione
	 * shared, ovvero quella condivisa da più dao e aperta mediante un
	 * DaoSession.
	 * 
	 * @return <code>true</code> se il dao è utilizzato in una connessione
	 *         shared.
	 */
	public boolean isInSharedConnection() {
		if (daoSession != null && daoSession.isSharedConnectionOpened()) {
			return true;
		}

		return false;
	}

	/**
	 * Se <code>true</code> indica che il dao è utilizzato in una transazione
	 * aperta mediante un DaoSession.
	 * 
	 * @return <code>true</code> se il dao è utilizzato in una transazione.
	 */
	public boolean isInTransaction() {
		if (daoSession != null && daoSession.isTransactionOpened()) {
			return true;
		}

		return false;
	}

	/**
	 * Restituisce <code>true</code> se è stato abilitato il sistema di log
	 * delle query eseguite. In caso contrario restituisce <code>false</code>.
	 * 
	 * @return true if printmode
	 */
	public boolean isPrintMode() {
		return printMode && globalPrintMode;
	}

	protected static boolean globalPrintMode;

	/**
	 * Dato l'elenco dei bean messi in elenco bean, crea una lista di entity
	 * bean il cui tipo è passato come parametro.
	 * 
	 * @param classDestination
	 * @param <B>
	 * @return elenco dei bean
	 * @throws Exception
	 */
	public <B> List<B> map(Class<B> classDestination) throws Exception {
		DaoMap map = sqlMapper.getMap(DaoMap.MAP_ALL_ATTRIBUTES);

		if (map == null) {
			return null;
		}

		return map.execute(elencoBean, entityBeanClass, classDestination);
	}

	/**
	 * Effettua la copia da un entity bean ad un altro di tutti gli attributi
	 * possibili
	 * 
	 * @param source
	 * @param destination
	 * @return <code>true</code> se la mappatura è andata bene
	 * @throws Exception
	 */
	public boolean map(Object source, Object destination) throws Exception {
		DaoMap map = sqlMapper.getMap(DaoMap.MAP_ALL_ATTRIBUTES);

		if (map != null) {
			map.execute(source, destination);
		}

		return true;
	}

	/**
	 * Data una lista di entity bean, crea una lista di entity bean il cui tipo
	 * è passato come parametro. Si utilizza la mappa di default, quindi si
	 * cerca di copiare tutti gli attributi.
	 * 
	 * @param listSource
	 * @param classDestination
	 * @param <B>
	 * @return lista dei bean
	 * @throws Exception
	 * 
	 */
	public <B> List<B> map(List<E> listSource, Class<B> classDestination) throws Exception {
		DaoMap map = sqlMapper.getMap(DaoMap.MAP_ALL_ATTRIBUTES);

		if (map == null) {
			return null;
		}

		return map.execute(listSource, entityBeanClass, classDestination);
	}

	/**
	 * Dato l'elenco dei bean messi in elenco bean, crea una lista di entity
	 * bean il cui tipo è passato come parametro. Si utilizza la mappa passata
	 * come paraemtro.
	 * 
	 * @param mapName
	 * @param classDestination
	 * @param <B>
	 * @return elenco dei bean
	 * @throws Exception
	 * 
	 */
	public <B> List<B> map(String mapName, Class<B> classDestination) throws Exception {
		DaoMap map = sqlMapper.getMap(mapName);

		if (map == null) {
			return null;
		}

		return map.execute(elencoBean, entityBeanClass, classDestination);
	}

	/**
	 * Effettua una copia degli attributi dell'entity <code>source</code> 
	 * all'entity <code>destination</code> in base alle regola di mappatura
	 * <code>mapName</code>.
	 * 
	 * @param mapName
	 * @param source
	 * @param destination
	 * 
	 * @return <code>true</code> se la mappatura è andata bene
	 * @throws Exception
	 *             in caso di errore
	 */
	public boolean map(String mapName, Object source, Object destination) throws Exception {
		DaoMap map = sqlMapper.getMap(mapName);

		if (map != null) {
			map.execute(source, destination);
		}

		return true;
	}

	/**
	 * Data una lista di entity bean, crea una lista di entity bean il cui tipo
	 * è passato come parametro. Si utilizza la mappa passata come paraemtro.
	 * 
	 * @param mapName
	 * @param listSource
	 * @param classDestination
	 * @param <B>
	 * @return lista di entity bean
	 * @throws Exception
	 * 
	 */
	public <B> List<B> map(String mapName, List<E> listSource, Class<B> classDestination) throws Exception {
		DaoMap map = sqlMapper.getMap(mapName);

		if (map == null) {
			return null;
		}

		return map.execute(listSource, entityBeanClass, classDestination);
	}

	/**
	 * Aggiorna il byte array di dati passato in un campo blob utilizzando la
	 * chiave d query SQL_UPDATE_BLOB la quale deve avere come primo parametro
	 * il campo blob da aggiornare e come ultimo la
	 * 
	 * @ulchiave primaria@ul (id) su cui viene fatta la where di update
	 * 
	 * @param id
	 *            chiave primaria
	 * @param blob
	 *            byte array contenente i dati da memorizzare nel blob
	 * @return boolean true = success
	 * @throws Exception
	 *             di tipo SQL
	 * 
	 *             public boolean memorizzaBlob(Long id, byte[] blob) throws
	 *             Exception { Log log = getLog(); boolean ok = false; if
	 *             (blob != null && id != null && id != EntityBean.DEFAULT_ID) {
	 *             Connection conn = null; PreparedStatement ps = null; try {
	 *             conn = getConnection(); ps =
	 *             conn.prepareStatement(getQuerySQL(SQL_UPDATE_BLOB));
	 * 
	 *             int length = blob.length; ByteArrayInputStream inputStream =
	 *             new ByteArrayInputStream(blob); ps.setBinaryStream(1,
	 *             inputStream, length);
	 * 
	 *             ps.setObject(2, id);
	 * 
	 *             ok = (ps.executeUpdate() > 0); } catch (SQLException e) {
	 *             log.fatal(e.getMessage());
	 * 
	 *             throw (new SQLException(e.getMessage())); } finally {
	 *             DbUtils.closeQuietly(conn, ps, null); } } return ok; }
	 */

	/**
	 * Visualizza un messaggio a livello di info.
	 * 
	 * @param msg
	 *            messaggio da visualizzare
	 */
	protected void printDebugSQL(String msg) {
		printDebugSQL(msg, PrintDebugType.INFO);
	}

	/**
	 * Tipo di informazione da debuggare
	 * 
	 * @author Francesco Benincasa (908099)
	 * @date 08/nov/07, 12:22:23
	 * 
	 */
	protected enum PrintDebugType {
		/**
		 * livello di debug
		 */
		DEBUG,
		/**
		 * livello di informazione
		 */
		INFO,
		/**
		 * livello di warning
		 */
		WARN,
		/**
		 * livello di errore
		 */
		ERROR,
		/**
		 * livello fatale
		 */
		FATAL
	};

	/**
	 * Visualizza un messaggio su log mediante un livello di importanza definito
	 * mediante parametro.
	 * 
	 * @param msg
	 *            messaggio da visualizzare
	 * @param level
	 *            tipo di messaggio.
	 */
	protected void printDebugSQL(String msg, PrintDebugType level) {
		if (isPrintMode()) {
			Log log = getLog();

			if (level == PrintDebugType.DEBUG) {
				log.debug(msg);
			} else if (level == PrintDebugType.INFO) {
				log.info(msg);
			} else if (level == PrintDebugType.WARN) {
				log.warn(msg);
			} else if (level == PrintDebugType.ERROR) {
				log.error(msg);
			} else if (level == PrintDebugType.FATAL) {
				log.fatal(msg);
			}
		}
	}

	/**
	 * Visualizza un messaggio su log a livello di info, fermo restando che deve
	 * essere abilitato il DebugMode per i dao.
	 * 
	 * @param sql
	 *            codice sql da visualizzare
	 * @param parms
	 *            elenco dei parametri della query
	 * @param paramNames
	 *            elenco dei nomi dei parametri.
	 */
	protected void printDebugSQL(String sql, Object[] parms, List<SqlParameter> paramNames) {

		SqlParameter dsp;
		Object value;
		String tempType;
		if (isPrintMode()) {
			Log log = getLog();
			log.info(sql);

			if (parms == null) {
				// non visualizza nulla
			} else if (parms != null && paramNames == null) {
				log.debug("Numbers of parameters: " + parms.length);
				for (int c = 0; c < parms.length; c++) {
					value = parms[c];
					tempType = ReflectUtil.getSimpleClassName(value);
					log.info("Parameter " + (c + 1) + "='" + value + "' , Value-Type: " + tempType);
				}
			} else if (parms != null && paramNames != null) {
				log.debug("Numbers of parameters: " + parms.length);
				for (int c = 0; c < parms.length; c++) {
					value = parms[c];
					dsp = paramNames.get(c);
					tempType = ReflectUtil.getSimpleClassName(value);

					log.info("Parameter " + (c + 1) + "='" + value + "' , Name: " + dsp.toString() + " , Value-Type: " + tempType + " , Param-Type: " + dsp.getType());
				}
			}
		}
	}
	
	/**
	 * Print connection hashcode 
	 * @param conn
	 */
	protected void printConnectionInfo(Connection conn, boolean transaction)
	{
		if (isPrintMode()) {
			String trans=transaction?"transaction/shared ":"atomic";
				printDebugSQL("Dao: used "+trans+" connection with hashcode = "+conn.hashCode(), PrintDebugType.INFO);
		}
	}

	/**
	 * Visualizza lo stack
	 */
	protected void printDebugStack() {
		if (isPrintMode() && stackTraceDiagnosticHandler.isEnabled()) {
			List<String> lista = StackTraceDiagnosticHelper.getStackInfoList(stackTraceDiagnosticHandler);

			for (String item : lista) {
				printDebugSQL(item, PrintDebugType.INFO);
			}
		}
	}

	/**
	 * Prende una stringa sql e rimpiazza la stringa [%LANG%] con il valore
	 * della lingua attualmente utilizzata dal dao. [%LANG%] viene sostituito
	 * con _<CODICE LINGUA>
	 * 
	 * @param sql
	 *            sql senza LANG processato
	 * @return sql con LANG processato
	 */
	protected String processLanguage(String sql) {
		// se non è definito un language restituiamo la stringa as is.
		// if (language==null || language.length()==0) return sql;
		String sLang = StringUtility.nvl(language);

		Matcher m = LANGUAGE_TOKEN.matcher(sql);
		StringBuffer sb = new StringBuffer();
		String r = (sLang.equals("")) ? "" : "_".concat(sLang);
		while (m.find())
			m.appendReplacement(sb, r);
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * Esegue una query di select.
	 * 
	 * @param queryName
	 *            nome della query da eseguire
	 * @param dto
	 *            data transfer object, ovvero l'oggetto che contiene i
	 *            parametri della query
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, EntityBean dto) throws Exception {
		return read(queryName, null, dto, null, null);
	}

	/**
	 * Esegue una pipeline, fornendo il suo nome ed l'entity bean in ingresso.
	 * 
	 * @param pipelineName
	 *            nome della pipeline
	 * @param dto
	 *            entity bean in ingresso
	 * @throws Exception
	 *             in caso di errore
	 */
	public final void runPipeline(String pipelineName, EntityBean dto) throws Exception {
		PipelineRuntime.run(this, pipelineName, dto);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param dto
	 *            oggetto contenente i parametri della query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, EntityBean dto, Object[] indexedParameters) throws Exception {
		return read(queryName, null, dto, indexedParameters, null);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param dto
	 *            oggetto contenente i parametri della query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @param classResultType
	 *            eb di ritorno
	 * @return numero di elementi letti dalla query
	 * 
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, EntityBean dto, Object[] indexedParameters, Class<?> classResultType) throws Exception {
		return read(queryName, null, dto, indexedParameters, classResultType);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @param classResultType
	 *            eb di ritorno
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	@SuppressWarnings("unchecked")
	public final int read(String queryName, Object[] indexedParameters, Class classResultType) throws Exception {
		return read(queryName, null, null, indexedParameters, classResultType);
	}

	public final int read(String queryName, Object[] indexedParameters) throws Exception {
		return read(queryName, null, null, indexedParameters, null);
	}

	public final int read(String queryName) throws Exception {
		return read(queryName, null, null, null, null);
	}

	/**
	 * Esegue una query di tipo select
	 * 
	 * @param queryName
	 *            nome della query
	 * @param moreSql
	 *            codice aggiuntivo da accordare alla query
	 * @param dto
	 *            entity bean contenent i parametri della query
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, String moreSql, EntityBean dto) throws Exception {
		return read(queryName, moreSql, dto, null, null);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param moreSql
	 *            codice aggiuntivo da accodare alla query
	 * @param dto
	 *            oggetto contenente i parametri della query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, String moreSql, EntityBean dto, Object[] indexedParameters) throws Exception {
		return read(queryName, moreSql, dto, indexedParameters, null);
	}

	/**
	 * Esegue una query di tipo select. L'utente che vuole eseguire la query
	 * viene preso dall'attributo <code>user</code> del Dao.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param moreSql
	 *            codice aggiuntivo da accodare alla query
	 * @param dto
	 *            entity bean contenente i parametri della query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @param classResultType
	 *            tipo di entity bean restituito dalla query
	 * @return numero di elementi letti dalla query
	 * 
	 * @throws Exception
	 *             in caso di errore
	 */
	public int read(String queryName, String moreSql, Object dto, Object[] indexedParameters, Class<?> classResultType) throws Exception {
		Log log = getLog();
		// Object resultType = null;
		SimpleQuery ret = null;

		if (dto == null) {
			// se non esiste lo creiamo del tipo che
			// specializza il dao
			dto = newBeanInstance(); 
		}

		if (indexedParameters == null)
			indexedParameters = new Object[0];

		if (classResultType == null) {
			// Se non e' definito il result type
			// prendiamo un nuovo bean gestito dal dao
			classResultType = entityBeanClass;
		}

		// prendiamo da eb i nomi degli attributi in esso definiti
		if (sqlMapper == null) {
			DaoException ex = new DaoException("The definition of the dao " + daoClassName + " does not exist!");
			log.error(ex);
			throw (ex);
		}
		SqlQuery query = sqlMapper.getQuery(queryName);
		if (query == null) {
			printDebugStack();
			DaoException ex = new DaoException("In the dao definition " + daoClassName + " the query " + queryName + " does not exist!");
			log.error(ex);
			throw (ex);
		}

		SqlQuery moreSqlQuery = null;

		moreSql = StringUtility.nvl(moreSql);
		if (moreSql.length() > 0) {
			moreSqlQuery = SqlParser.parseQuery(moreSql);
		}

		SqlQuery orderSqlQuery = null;
		// prendiamo con priorità l'order da codice o l'order in xml
		orderByName = StringUtility.nvl(orderByName).trim().toUpperCase();
		orderByName = orderByName.length() == 0 ? query.getOrderByName().trim().toUpperCase() : orderByName;

		if (orderByName.length() != 0) {
			orderSqlQuery = sqlMapper.getQuery(orderByName);

			if (orderSqlQuery == null) {
				DaoException ex = new DaoException("In the dao definition " + daoClassName + " the query for order " + orderByName + " does not exist!");
				log.error(ex);
				throw (ex);
			}

		}

		if (!query.isPreconditionRespected(user, dto, indexedParameters, variables)) {
			DaoException ex = new DaoException("In the dao definition " + daoClassName + " the precondition " + query.getPrecondition() + " for the query " + query.getName()
					+ " is not satisfeid!");
			log.error(ex);
			throw (ex);
		}

		ret = DaoRuntime.getSimpleQuery(parameterConverter, user, dto, indexedParameters, variables, query, moreSqlQuery, orderSqlQuery);

		// vecchio codice da togliere prima o poi
		orderBy = "";
		// puliamo l'order by
		orderByName = "";

		return readSQL(queryName, ret.preparedSql, ret.preparedParameters, ret.preparedParameterNames, classResultType);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param moreSql
	 *            codice aggiuntivo da accodare alla query
	 * @param indexedParameters
	 *            array di parametri della query
	 * @return numero di elementi letti dalla query
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, String moreSql, Object[] indexedParameters) throws Exception {
		return read(queryName, moreSql, null, indexedParameters, null);
	}

	/**
	 * Esegue una query di tipo select.
	 * 
	 * @param queryName
	 *            nome della query
	 * @param dto
	 *            oggetto contenente i parametri della query
	 * @param classResultType
	 *            eb di ritorno
	 * @return numero di elementi letti dalla query
	 * 
	 * @throws Exception
	 *             in caso di errore
	 */
	public final int read(String queryName, EntityBean dto, Class<?> classResultType) throws Exception {
		return read(queryName, null, dto, null, classResultType);
	}

	/**
	 * E' il metodo alla base di tutte le operazioni di tipo SELECT. Il codice
	 * sql che arriva a questo metodo è già stato elaborato, quindi tutti i
	 * parametri sono stati analizzati e sostituiti con il ? (nel caso di db
	 * oracle).
	 * <p>
	 * Anche tutte gli statement condizionali sono stati analizzati e rimossi o
	 * valorizzati a seconda del fatto che le precondizioni fossero soddisfatte
	 * o meno.
	 * 
	 * @param sql
	 *            stringa sql da eseguire.
	 * @param whereConditions
	 *            clausole di where da aggiungiungere al codice sql
	 * @param parms
	 *            valori dei parametri da definire per l'intera query.
	 * 
	 * @return restituisce il numero di elementi recuperati.
	 * @throws Exception
	 *             any exception
	 */
	protected final int readSQL(String sql, Object[] parms, Class<?> resultType) throws Exception {
		return readSQL(null, sql, parms, null, resultType);
	}

	/**
	 * E' il metodo alla base di tutte le operazioni di tipo SELECT. Il codice
	 * sql che arriva a questo metodo è già stato elaborato, quindi tutti i
	 * parametri sono stati analizzati e sostituiti con il ? (nel caso di db
	 * oracle).
	 * <p>
	 * Anche tutte gli statement condizionali sono stati analizzati e rimossi o
	 * valorizzati a seconda del fatto che le precondizioni fossero soddisfatte
	 * o meno.
	 * 
	 * @param sqlName
	 *            indica il nome della query che si sta eseguendo. Serve per i
	 *            mesasggi di diagnostica. Può essere anche messo a
	 *            <code>null</code>.
	 * @param sql
	 *            stringa sql da eseguire.
	 * @param whereConditions
	 *            clausole di where da aggiungiungere al codice sql
	 * @param parms
	 *            valori dei parametri da definire per l'intera query.
	 * 
	 * @return restituisce il numero di elementi recuperati.
	 * @throws Exception
	 *             any exception
	 */
	@SuppressWarnings("unchecked")
	protected final int readSQL(String sqlName, String sql, Object[] parms, List<SqlParameter> paramName, Class resultType) throws Exception {

		Log log = getLog();

		// il tipo di risultato lo prendiamo come parametro
		if (resultType == null)
			resultType = entityBeanClass;

		// facciamo un controllo, se sql non contiene un codice valido, allora
		// usciamo senza far nulla
		sql = StringUtility.nvl(sql).trim();
		if (sql.length() == 0) {
			// "ATTENZIONE: Si e' cercato di recuperare una codice SQL vuoto.
			// Forse il nome della query none' corretto?");
			String sMsg = "WARNING Dao: " + daoClassName + " Query: " + StringUtility.nvl(sqlName, "<Unknown>")
					+ " The sql string is empty. Perhaps the name of the query is wrong?";
			printDebugSQL(sMsg, PrintDebugType.WARN);

			elencoBean = new LinkedList();
			createResultBean();

			return 0;
		}

		int res = RETURN_ERROR;
		// ResultSetHandler rsHandler = new
		// EntityBeanRsHandler(dettaglioBean.getClass());

		EntityBeanRsHandler rsHandler = new EntityBeanRsHandler(resultType);

		// impostiamo il gestore dell'evento ricezione del bean
		rsHandler.setOnGetEntityEventHandler(eventHandler);
		rsHandler.setDaoName(daoClassName);
		
		if (StringUtility.nvl(sqlName).length()>0)
		{
			SqlQuery query=sqlMapper.getQuery(sqlName);
			
			String conversionMapName=StringUtility.nvl(query.getConversionMapName());
			
			if (conversionMapName.length()>0)
			{
				DaoMap map=sqlMapper.getMap(conversionMapName);
				rsHandler.setConversionMap(map);
			}
		}

		Connection conn = null;
		try {
			conn = (this.transaction == null ? getConnection() : this.transaction);

			// processiamo la lingua
			String sSql = processLanguage(sql);

			printDebugStack();

			printConnectionInfo(conn, transaction!=null);
			printDebugSQL("Dao: " + daoClassName + " Query: " + StringUtility.nvl(sqlName, "<Unknown>") + " " + " read SQL: " + sSql, parms, paramName);

			// puliamo l'elenco delle colonne recuperate
			columns = null;
			List<E> l = (List<E>) QueryRunner.query(conn, sSql, rsHandler, parms);			

			// memorizza l'array delle colonne recuperate
			columns = rsHandler.getColumns();

			printDebugSQL("# row read: " + l.size());
			printDebugSQL("Time for execute sql: " + rsHandler.getExecutionTimeOfSql() + " ms, Time for populate the eb list: " + rsHandler.getExecutionTimeOfRetriveList()
					+ " ms, Time total: " + rsHandler.getTotalExecutionTime() + " ms");

			if (l != null) {
				res = l.size();
				this.elencoBean = l;
				iteratorElencoBean = elencoBean.iterator();

				if (res > 0) {
					// ASSERT: lo imposto solo se trovo qualcosa
					dettaglioBean = l.get(0);
				} else {
					// altrimenti creiamo un nuovo dettaglio bean
					// dettaglioBean=resultType.newInstance();
					createResultBean();
				}
			} else {
				// Nel caso non c'e' niente creiamo una lista vuota
				elencoBean = new LinkedList();
				iteratorElencoBean = elencoBean.iterator();
				// il dettaglio bean deve essere del tipo desiderato
				// dettaglioBean = resultType.newInstance();
				createResultBean();
			}
		} catch (SQLException e) {
			log.fatal(e.getMessage());
			throw (exceptionTranslator.translate(sql, e, DaoRuntimeHelper.getDatabaseInformation(conn)));
		} catch (Exception e) {
			log.fatal(e.getMessage());
			throw (new DaoException(e.getMessage(), e));
		} finally {
			if (this.transaction == null) {
				DbUtils.closeQuietly(conn);
			}
		}
		return res;
	}



	/**
	 * @param daoName
	 * @return
	 */
	protected SqlMapper getDaoDefinition(String daoName) {
		SqlMapper ret = null;
		try {
			ret = DaoStars.getInstance().getSqlMapper(daoName);

			if (ret == null)
				throw (new DaoException("The definition of the dao " + daoName + " is missed."));

		} catch (Exception e) {
			Log log = getLog();
			log.fatal("Error during the initialization of the dao " + daoName + " :" + e.toString());
		}

		return ret;
	}

	/**
	 * setter del dao session
	 * 
	 * @param daoSession
	 *            the daoSession to set
	 */
	@Override
	public void setDaoSession(DaoSession daoSession) {
		this.daoSession = daoSession;
	}
	
	/**
	 * Setter della lingua (accetta un Local).
	 * 
	 * @param lang
	 *            nuovo valore
	 */
	public void setLanguage(Locale lang) {
		setLanguage((lang != null) ? lang.toString().toUpperCase() : null);
	}

	/**
	 * Setter della lingua
	 * 
	 * @param lang
	 *            nuovo valore
	 */
	public void setLanguage(String lang) {
		this.language = lang;

		// aggiungiamo _ davanti alla variabile.
		if (StringUtility.nvl(lang).length() > 0) {
			lang = "_" + lang;
		}

		setVariable("LANGUAGE", lang);
	}

	/**
	 * Setter dell'attributo onGetEntityEventHandler.
	 * 
	 * @param onGetEntityEventHandler
	 *            the onGetEntityEventHandler to set
	 */
	public void setEventHandler(DaoEventHandler onGetEntityEventHandler) {
		this.eventHandler = onGetEntityEventHandler;
	}

	/**
	 * @param orderBy
	 *            the orderBy to set
	 */
	public void setOrderBy(String orderBy) {
		this.orderBy = orderBy;
	}

	/**
	 * @param orderByName
	 *            the orderByName to set
	 */
	public void setOrderByName(String orderByName) {
		this.orderByName = orderByName;
	}

	/**
	 * @param value
	 *            the parameterConverter to set
	 */
	public void setParameterConverter(ParameterConverter value) {
		this.parameterConverter = value;
	}

	/**
	 * Imposta il print mode a <code>true</code> o a <code>false</code>.
	 * 
	 * @param value
	 *            da impostare
	 */
	public void setPrintMode(boolean value) {
		this.printMode = value;
	}

	/**
	 * @param stackTraceDiagnosticHandler
	 *            the stackTraceDiagnosticHandler to set
	 */
	public void setStackTraceDiagnosticHandler(StackTraceDiagnosticHandler stackTraceDiagnosticHandler) {
		this.stackTraceDiagnosticHandler = stackTraceDiagnosticHandler;
	}

	/**
	 * Setter della transazione in caso di transazione.
	 * 
	 * @param conn
	 *            connessione
	 * @throws DaoException
	 */
	@Override
	public void setTransaction(Connection conn) throws DaoException {
		if (conn != null && transaction != null)
			throw (new DaoException("This instance of dao " + this.daoClassName + " is already inserted in a transaction/condivided connection"));
		// "Si e' cercato di aggiungere ad una transazione/connessione condivisa
		// un dao gia' inserito precedentemente in una transazione/connessione
		// condivisa."));
		transaction = conn;
	}

	/**
	 * Setup della classe
	 * 
	 * @param eType
	 *            tipo di entity bean gestito dal dao
	 */
	public void setUp(Class<E> eType) {
		setUp(null, eType);
	}
 
	/**
	 * Setup della classe
	 * 
	 * @param eType
	 *            tipo di entity bean gestito dal dao
	 */
	public void setUp(String daoDefinitionName, Class<E> eType) {
		printMode = true;

		user = null;
		eventHandler = null;
		enabledRowLimit = false;

		// inizializziamo il dettaglio bean e l'elenco bean
		dettaglioBean = Helper.makeObject(eType);

		elencoBean = new LinkedList<E>();
		iteratorElencoBean = elencoBean.iterator();

		// Impostiamo il nome della classe.
		// Utile in seguito.
		entityBeanClassName = dettaglioBean.getClass().getName();
		entityBeanClass = eType; // classe gestita dal dao
		// Tutti i dao di default devono impostare il print mode a true.
		// Il global di default e' impostato a false, quindi comunque non stampa
		// nulla.
		// se imposto a printmode a false, solo i nuovi DAO avranno la
		// possibilita'
		// di visualizzare le query.
		setPrintMode(true);

		// associamo dal dao le query.
		daoClassName = "";
		Dao daoAssociation = getClass().getAnnotation(Dao.class);
		if (daoAssociation!=null && StringUtility.nvl(daoAssociation.name()).trim().length() > 0) {
			// ASSERT: il nome della definizione e' stata definita mediante
			// annotation
			daoClassName = daoAssociation.name().trim();
		} else if (StringUtility.nvl(daoDefinitionName).trim().length() > 0) {
			// ASSERT: il nome della definizione e' stata definita mediante
			// parametro del metodo
			daoClassName = daoDefinitionName.trim();
		} else {
			// ASSERT: nell'ultimo caso cerchiamo di ricavarlo direttamente dal
			// nome dell'entity bean
			daoClassName = getDaoNameFromEntity(dettaglioBean);
		}
		sqlMapper = getDaoDefinition(daoClassName);

		setParameterConverter(DaoStars.getInstance().getParameterConverter());
		setStackTraceDiagnosticHandler(DaoStars.getInstance().getStackTraceDiagnosticHandler());
		exceptionTranslator = DaoStars.getInstance().getSQLExceptionTranslator();
		language = ""; // lo impostiamo di default senza gestione lingua

		variables = new HashMap<String, String>();
		Map<String, String> temp = sqlMapper.getVariableSet();

		// copia le variabili dalla definizione
		for (String varKey : temp.keySet()) {
			variables.put(varKey, temp.get(varKey));
		}
	}
	
	/**
	 * Ripristina i valori delle variabili a quelle definite nella definizione.
	 */
	public void resetVariables()
	{
		Map<String, String> temp = sqlMapper.getVariableSet();
		
		for (String varKey : temp.keySet()) {
			variables.put(varKey, temp.get(varKey));
		}
	}
	

	/**
	 * Setter dell'attributo user
	 * 
	 * @param user
	 *            the user to set
	 */
	public void setUser(UserIdentity user) {
		this.user = user;
	}

	/**
	 * Imposta una variabile. Il nome viene impostato in UPPERCASE. Nel caso in
	 * cui il valore sia null, viene impostato con stringa vuota.
	 * 
	 * @param name
	 *            nome della variabile
	 * @param value
	 *            valore della variabile
	 */
	public void setVariable(String name, String value) {
		name = StringUtility.nvl(name).toUpperCase();
		value = StringUtility.nvl(value);

		variables.put(name, value);
	}

	/**
	 * Getter dell'attributo enabledRowLimit
	 * 
	 * @return the enabledRowLimit
	 */
	public boolean isEnabledRowLimit() {
		return enabledRowLimit;
	}

	/**
	 * Setter dell'attributo enabledRowLimit
	 * 
	 * @param enabledRowLimit
	 *            the enabledRowLimit to set
	 */
	public void setEnabledRowLimit(boolean enabledRowLimit) {
		this.enabledRowLimit = enabledRowLimit;
	}

	/**
	 * Getter dell'attributo connectionName
	 * 
	 * @return the connectionName
	 */
	public String getConnectionName() {
		return connectionName;
	}

	/**
	 * Setter dell'attributo connectionName
	 * 
	 * @param connectionName
	 *            the connectionName to set
	 */
	public void setConnectionName(String connectionName) {
		this.connectionName = connectionName;
	}

}
